`include "defines.v"

module Forwarding (
    input  wire [`REGFILE_ADDR_LEN-1:0] rs1_r_addr,
    input  wire [`REGFILE_ADDR_LEN-1:0] rs2_r_addr,
    input  wire [`CSR_ADDR_LEN-1:0]     csr_r_addr,

    input  wire [`REGFILE_ADDR_LEN-1:0] rd_w_addr_ex,
    input  wire [`REGFILE_ADDR_LEN-1:0] rd_w_addr_mem,
    input  wire [`REGFILE_ADDR_LEN-1:0] rd_w_addr_wb,
    input  wire [`CSR_ADDR_LEN-1:0]     csr_w_addr_ex,
    input  wire [`CSR_ADDR_LEN-1:0]     csr_w_addr_mem,
    input  wire [`CSR_ADDR_LEN-1:0]     csr_w_addr_wb,
    
    input  wire                         rd_w_en_ex,
    input  wire                         rd_w_en_mem,
    input  wire                         rd_w_en_wb,
    input  wire                         rd_w_src_ex,
    input  wire                         rd_w_src_mem,
    input  wire                         csr_w_en_ex,
    input  wire                         csr_w_en_mem,
    input  wire                         csr_w_en_wb,
    output wire                         forward_rs1_flag,
    output wire                         forward_rs2_flag,
    output wire                         forward_csr_flag,

    input  wire [`XLEN-1:0]             rd_w_data_ex,
    input  wire [`XLEN-1:0]             rd_w_data_mem,
    input  wire [`XLEN-1:0]             rd_w_data_wb,
    input  wire [`MXLEN-1:0]            csr_w_data_ex,
    input  wire [`MXLEN-1:0]            csr_w_data_mem,
    input  wire [`MXLEN-1:0]            csr_w_data_wb,
    output wire [`XLEN-1:0]             forward_rs1_data,
    output wire [`XLEN-1:0]             forward_rs2_data,
    output wire [`XLEN-1:0]             forward_csr_data
);
    //----------Forwarding Flags----------//
    wire forward_rs1_flag_ex;
    wire forward_rs1_flag_mem;
    wire forward_rs1_flag_wb;
    wire forward_rs2_flag_ex;
    wire forward_rs2_flag_mem;
    wire forward_rs2_flag_wb;
    wire forward_csr_flag_ex;
    wire forward_csr_flag_mem;
    wire forward_csr_flag_wb;

    assign forward_rs1_flag_ex = rd_w_en_ex && (rd_w_addr_ex == rs1_r_addr) && (|rd_w_addr_ex) && (~rd_w_src_ex);
    assign forward_rs2_flag_ex = rd_w_en_ex && (rd_w_addr_ex == rs2_r_addr) && (|rd_w_addr_ex) && (~rd_w_src_ex);
    assign forward_csr_flag_ex = csr_w_en_ex && (csr_w_addr_ex == csr_r_addr);

    assign forward_rs1_flag_mem = rd_w_en_mem && (rd_w_addr_mem == rs1_r_addr) && (|rd_w_addr_mem) && (~rd_w_src_mem);
    assign forward_rs2_flag_mem = rd_w_en_mem && (rd_w_addr_mem == rs2_r_addr) && (|rd_w_addr_mem) && (~rd_w_src_mem);
    assign forward_csr_flag_mem = csr_w_en_mem && (csr_w_addr_mem == csr_r_addr);

    assign forward_rs1_flag_wb = rd_w_en_wb && (rd_w_addr_wb == rs1_r_addr) && (|rd_w_addr_wb);
    assign forward_rs2_flag_wb = rd_w_en_wb && (rd_w_addr_wb == rs2_r_addr) && (|rd_w_addr_wb);
    assign forward_csr_flag_wb = csr_w_en_wb && (csr_w_addr_wb == csr_r_addr);

    assign forward_rs1_flag = forward_rs1_flag_ex | forward_rs1_flag_mem | forward_rs1_flag_wb;
    assign forward_rs2_flag = forward_rs2_flag_ex | forward_rs2_flag_mem | forward_rs2_flag_wb;
    assign forward_csr_flag = forward_csr_flag_ex | forward_csr_flag_mem | forward_csr_flag_wb;

    //----------Data Forwarding----------//
    assign forward_rs1_data = forward_rs1_flag_ex  ?  rd_w_data_ex   : 
                              forward_rs1_flag_mem ?  rd_w_data_mem  : 
                              forward_rs1_flag_wb  ?  rd_w_data_wb   :  64'h0  ;

    assign forward_rs2_data = forward_rs2_flag_ex  ?  rd_w_data_ex   :
                              forward_rs2_flag_mem ?  rd_w_data_mem  :
                              forward_rs2_flag_wb  ?  rd_w_data_wb   :  64'h0  ;

    assign forward_csr_data = forward_csr_flag_ex  ?  csr_w_data_ex   :
                              forward_csr_flag_mem ?  csr_w_data_mem  :
                              forward_csr_flag_wb  ?  csr_w_data_wb   :  64'h0  ;
                
endmodule